package framework.concurrent;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import framework.common.DeviceRequest;
import framework.common.DeviceResponse;
import framework.common.Client;
import framework.common.DeviceServer;

public class HubServer extends framework.sequential.HubServer implements
		Runnable {
	private Map<Client, BlockingQueue<DeviceResponse>> deviceResponses;
	private Map<String, BlockingQueue<DeviceRequest>> deviceRequests;

	public HubServer() {
		super();
		deviceRequests = new HashMap<String, BlockingQueue<DeviceRequest>>();
		deviceResponses = new HashMap<Client, BlockingQueue<DeviceResponse>>();
	}

	/**
	 * DeviceServerから応答を受けとる。外部応答キューに挿入できるまで待つ。 blocking put
	 *
	 * @param res
	 *            デバイス応答
	 */
	@Override
	public void putResponse(DeviceResponse res) {
		Client c = res.getRequest().getClient();
		if (deviceResponses.containsKey(c)) {
			try {
				System.out.println("putting a Response ...");
				deviceResponses.get(c).put(res);
				System.out.println("put a Response.");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			throw new RuntimeException("no such client");
		}
	}

	/**
	 * DeviceServerから応答を受けとる。外部応答キューに挿入できなければ戻る。 non-blocking put
	 *
	 * @param res
	 *            デバイス応答
	 */
	public boolean offerResponse(DeviceResponse res) {
		// FIXME 必ず挿入できるものと仮定して実装している
		putResponse(res);
		return true;
	}

	/**
	 * DeviceServerに要求を返す。内部応答キューから削除できるまで待つ。 blocking get
	 *
	 * @param deviceServer
	 *            デバイスサーバ
	 * @return デバイス要求
	 */
	public DeviceRequest takeRequest(DeviceServer deviceServer) {
		try {
			System.out.println("getting a Request ...");
			DeviceRequest req = deviceRequests
					.get(deviceServer.getDeviceName()).take();
			System.out.println("got a Request.");
			return req;
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}

	// non-blocking get
	public DeviceRequest pollRequest(DeviceServer deviceServer, long timeout,
			TimeUnit unit) {
		// TODO not implemented yet
		return null;
	}

	@Override
	public DeviceRequest getRequest(DeviceServer server) {
		return takeRequest(server);
	}

	// Clientから要求を受け取る
	// blocking put
	@Override
	public void putRequest(DeviceRequest req) {
		Client client = req.getClient();
		if (!deviceResponses.containsKey(client)) {
			BlockingQueue<DeviceResponse> queue = new LinkedBlockingQueue<DeviceResponse>();
			deviceResponses.put(client, queue);
		}
		String user = client.getUser();
		String deviceName = client.getDeviceName();
		if (hasDevice(user, deviceName)) {
			try {
				System.out.println("putting a Request ...");
				deviceRequests.get(deviceName).put(req);
				System.out.println("put a Request.");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			throw new RuntimeException("no such device " + deviceName);
		}

	}

	// non-blocking put
	public boolean offerRequest(DeviceRequest req) {
		// FIXME 必ず挿入できるものと仮定して実装している
		putRequest(req);
		return true;
	}

	/**
	 * Clientに応答を返す。外部応答キューから削除できるまで待つ。 blocking get
	 *
	 * @param client
	 *            クライアント
	 * @return デバイス応答
	 */
	public DeviceResponse takeResponse(Client client) {
		// if (deviceResponses.get(client).size() > 0)
		try {
			System.out.println("getting a Response ...");
			DeviceResponse res = deviceResponses.get(client).take();
			System.out.println("got a Response.");
			return res;
		} catch (InterruptedException e) {
			throw new RuntimeException("no such client");
		}
		// return null;
	}

	// non-blocking get
	public DeviceResponse pollResponse(Client client, long timeout,
			TimeUnit unit) {
		// TODO not implemented yet
		return null;
	}

	@Override
	public DeviceResponse getResponse(Client client) {
		return takeResponse(client);
	}

	// デバイスをuserの所有物として登録する
	@Override
	public void register(String user, DeviceServer dev) {
		super.register(user, dev);
		deviceRequests.put(dev.getDeviceName(),
				new LinkedBlockingQueue<DeviceRequest>());
	}

	@Override
	public void start() {
		// do nothing because HubServer is passive
	}

	@Override
	public void stop() {
		// do nothing because HubServer is passive
	}

}
